 /*
  *  参考
  *  https://search.gitee.com/?q=linux+spi+driver&skin=rec&type=repository
  */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/capability.h>
#include <linux/jiffies.h>
#include <linux/spi/spi.h>
#include <linux/mutex.h>
 
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/cdev.h>

#include <linux/gpio.h>

#define DEVICE_NAME "w25qxx"

static struct spi_device * w25qxx_spi;
static dev_t devnum;
static struct cdev *chr_dev;
static struct class *drv_class;
static struct device *device ;

#define Dummy_Byte          0xFF
#define W25X_DeviceID		0xAB 
#define W25X_JedecDeviceID		0x9F


//#define  sFLASH_ID              0xEF3015   //W25X16
#define  sFLASH_ID              0xEF4015	 //W25Q16
//#define  sFLASH_ID              0XEF4018   //W25Q128
//#define  sFLASH_ID              0XEF4017    //W25Q64


int SPIFlashReaddeviceID(void)
{
	int Temp;
	unsigned  char tx_buf[5];
	unsigned  char rx_buf[5];

	tx_buf[0]=W25X_DeviceID;
	tx_buf[1]=Dummy_Byte;
	tx_buf[2]=Dummy_Byte;
	tx_buf[3]=Dummy_Byte;
	tx_buf[4]=Dummy_Byte;

	spi_write_then_read(w25qxx_spi, tx_buf, 5, rx_buf, 5);

	return Temp = rx_buf[4];
}


int SPIFlashReadID(void)
{
	int Temp;
	unsigned  char tx_buf[4];
	unsigned  char rx_buf[4];

	tx_buf[0]=W25X_JedecDeviceID;
	tx_buf[1]=Dummy_Byte;
	tx_buf[2]=Dummy_Byte;
	tx_buf[3]=Dummy_Byte;

	spi_write_then_read(w25qxx_spi, tx_buf, 4, rx_buf, 4);

	return Temp = (rx_buf[1] << 16) | (rx_buf[2] << 8) | rx_buf[3];
}

static void SPIFlashWriteEnable(int enable)
{
    unsigned char val = enable ? 0x06:0x04;

    spi_write(w25qxx_spi, &val, 1);
}

static unsigned char SPIFlashReadStatusReg1(void)
{
    unsigned char val,cmd=0x05;
    spi_write_then_read(w25qxx_spi, &cmd , 1, &val, 1);
    return val;
}

static unsigned char SPIFlashReadStatusReg2(void)
{
    unsigned char val,cmd=0x35;
    spi_write_then_read(w25qxx_spi, &cmd , 1, &val, 1);
    return val;
}

static void SPIFlashWaitWhenBusy(void)
{
    while (SPIFlashReadStatusReg1() & 1)
    {
        /*休眠一段时间*/
        set_current_state(TASK_INTERRUPTIBLE);
        schedule_timeout(HZ/100);/*休眠10ms后再次判断*/
    }
}

static void SPIFlashWriteStatusReg(unsigned char reg1, unsigned char reg2)
{    
	unsigned  char tx_buf[3];

	SPIFlashWriteEnable(1);  

	tx_buf[0]=0x01;
	tx_buf[1]=reg1;
	tx_buf[2]=reg2;

	spi_write(w25qxx_spi, tx_buf, 3);
	SPIFlashWaitWhenBusy();
}


/* erase 4K */
void SPIFlashEraseSector(unsigned int addr)
{

    unsigned  char tx_buf[4];
    tx_buf[0]= 0x20;
    tx_buf[1]=addr >> 16;
    tx_buf[2]=addr >> 8;
    tx_buf[3]=addr & 0xff;

    SPIFlashWriteEnable(1);  

    spi_write(w25qxx_spi, tx_buf, 4);

    SPIFlashWaitWhenBusy(); 
}

/* Write */
void SPIFlashWrite(unsigned int addr, unsigned char *buf, int len)
{
    unsigned  char tx_buf[4];
    
    struct spi_transfer	t[] = {
        {
            .tx_buf	= tx_buf,
            .len	= 4,
        },
        {
            .tx_buf	= buf,
            .len	= len,
        },
    };
	struct spi_message	m;

    tx_buf[0]= 0x02;
    tx_buf[1]=addr >> 16;
    tx_buf[2]=addr >> 8;
    tx_buf[3]=addr & 0xff;

    SPIFlashWriteEnable(1);  
    spi_message_init(&m);
	spi_message_add_tail(&t[0], &m);
    spi_message_add_tail(&t[1], &m);
    spi_sync(w25qxx_spi, &m);
    SPIFlashWaitWhenBusy();
    
}

void SPIFlashRead(unsigned int addr, unsigned char *buf, int len)
{
     
    unsigned  char tx_buf[4];
    
    struct spi_transfer	t[] = {
        {
            .tx_buf	= tx_buf,
            .len	= 4,
        },
        {
            .rx_buf	= buf,
            .len	= len,
        },
    };
	struct spi_message	m;

    tx_buf[0]= 0x03;
    tx_buf[1]=addr >> 16;
    tx_buf[2]=addr >> 8;
    tx_buf[3]=addr & 0xff;

    spi_message_init(&m);
	spi_message_add_tail(&t[0], &m);
    spi_message_add_tail(&t[1], &m);
    spi_sync(w25qxx_spi, &m);
     
   
}


static int w25qxx_open(struct inode *inode, struct file *file)
{
	int flash_ID, devices_ID;
	file->private_data = w25qxx_spi;
	
	flash_ID = SPIFlashReadID();
	devices_ID = SPIFlashReaddeviceID();
	printk("flash_ID is %d devices id is %d\n",flash_ID,devices_ID);
	return 0;
}

static int w25qxx_read(struct file *file, char __user *buf, size_t count, loff_t * offset)
{
    unsigned char addr[2];
    unsigned char data[50];

    if(copy_from_user(addr, buf, 2)) {
        return -EFAULT;
    }

    SPIFlashRead(addr[0], data, addr[1]);

    if (copy_to_user(buf ,data, count) )
        return  -EFAULT;

    return 0 ;
}

static int w25qxx_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
{
    unsigned char data[50];

    if(copy_from_user(data, buf, count )) {
         return -EFAULT;
    }   
    SPIFlashEraseSector(data[0]);
    
    SPIFlashWrite(data[0],&data[1],count);
    
    return 0 ;
}


static struct file_operations w25qxx_fops = {
    .owner = THIS_MODULE,
    .open  = w25qxx_open,
    .read  = w25qxx_read,
    .write = w25qxx_write,
};


static int w25qxx_probe(struct spi_device * spi)
{
	int ret;
	printk("[%s][%d]\n",__FUNCTION__,__LINE__);
	//注册字符设备
	w25qxx_spi = spi;

	//获取一个主设备号
	ret = alloc_chrdev_region(&devnum, 0 , 1, "maz-spi");
	if(ret < 0) {
		printk("alloc_chrdev_region fail\n");
		goto err_alloc_chrdev_region;
	}
	printk("major = %d\n",MAJOR(devnum) );//主设备号
	
	//获取一个struct cdev结构体,这个结构体是自动释放的
	chr_dev = cdev_alloc();
	if(!chr_dev ) {
		goto err_cdev_alloc;
	}
	//初始化cdev
	
	cdev_init(chr_dev, &w25qxx_fops);
	chr_dev->owner = THIS_MODULE;
	//把这个cdev加入字符设备表

	ret = cdev_add(chr_dev, devnum, 1);
	if(ret) {
		printk("cdev_add fail");
		goto err_cdev_add;

	}

	//创建一个类
	drv_class = class_create(THIS_MODULE, "maz-w25qxx-spi");
	if(!drv_class) {
		printk("class_create fail\n");
		goto err_class_create;
	}
	//创建一个设备
	device = device_create(drv_class,NULL, devnum, NULL, DEVICE_NAME);
	if( !device) {
		printk("device_create fail \n");
		goto err_device_create;
	}
	return 0;
err_device_create:
    class_destroy(drv_class);
err_class_create:  
err_cdev_add:
     cdev_del(chr_dev);
err_cdev_alloc:
    unregister_chrdev_region(devnum, 1);
err_alloc_chrdev_region:
    return ret;
}
static int w25qxx_remove(struct spi_device *spi)
{
	printk("[%s][%d]\n",__FUNCTION__,__LINE__);
	device_destroy(drv_class, devnum);
	class_destroy(drv_class);
	cdev_del(chr_dev);
	unregister_chrdev_region(devnum, 1);
	return 0;
}

static const struct spi_device_id w25qxx_id_table[] = {
    { "w25qxx", 0 },
    {}
};

static struct spi_driver spi_flash_drv = {
    .driver = {
        .name = "spi_flash",
        .owner = THIS_MODULE,
    },
    .probe = w25qxx_probe,
    .remove  = w25qxx_remove,
    .id_table = w25qxx_id_table,
};

module_spi_driver(spi_flash_drv);
MODULE_LICENSE("GPL");

